<?php
namespace Common\Model;
use Think\Model;
use Think\Upload;

/**
 * 文件模型
 * 负责文件的下载和上传
 */

class FileModel extends Model{
    /**
     * 文件模型自动完成
     * @var array
     */
    protected $_auto = array(
        array('create_time', NOW_TIME, self::MODEL_INSERT),
    	array('update_time', NOW_TIME, self::MODEL_BOTH),
    	array('status', 1, self::MODEL_INSERT, 'string')
    );

    /**
     * 文件模型字段映射
     * @var array
     */
    protected $_map = array(
        'type' => 'mime',
    	'createTime' => 'create_time',
    	'updateTime' => 'update_time',
    	'fileType' => 'file_type',
    	'operatorId' => 'operator_id',
    	'operatorType' => 'operator_type',
    	'operatorName' => 'operator_name',
    );

    /**
     * 文件上传
     * @param  array  $files   要上传的文件列表（通常是$_FILES数组）
     * @param  array  $setting 文件上传配置
     * @param  string $driver  上传驱动名称
     * @param  array  $config  上传驱动配置
     * @return array           文件上传成功后的信息
     * @author 刘新
     */
    public function upload($files, $setting, $param, $driver = 'Local', $config = null){
        //$setting['callback'] = array($this, 'isFile');
    	$setting['removeTrash'] = array($this, 'removeTrash');

		//上传文件
		$Upload = new Upload($setting, $driver, $config);
        $info   = $Upload->upload($files);
		
        //测试代码
        //die(json_encode($info));
        
        //设置文件保存位置 
		$this->_auto[] = array('location', 'ftp' === strtolower($driver) ? 1 : 0, self::MODEL_INSERT);

		//文件上传成功，记录文件信息
        if($info) { 
            foreach ($info as $key => &$value) {
                if(count($param)>0){
                    $value = array_merge($value, $param);
                }
            	//文件对外访问路径
            	$value['url'] = substr($setting['rootPath'], 1).$value['savepath'].$value['savename']; 
                //文件在服务器上的绝对路径
            	$value['realpath'] = realpath(C('DOWNLOAD_UPLOAD.rootPath')) . '/' . $value['savepath'] . $value['savename'];
				
            	//设置文件名称
            	$value['name'] = rtrim($value['name'], '.' . $value['ext']);
            	
             	//记录文件信息 
                if($this->create($value)){
					if($value['id']) {
						$res = $this->save();
					} else {
						$res = $this->add();
						$value['id'] = $res;
					}

                	//TODO: 文件上传成功，但是记录文件信息失败，需记录日志
                	if(!res) {
	                	unset($info[$key]);
                	}
                }
            }
            
            //文件上传成功
            return $info; 
        } else {
            $this->error = $Upload->getError();
            return false;
        }
    }

    /**
     * 下载指定文件
     * @param  number  $root 文件存储根目录
     * @param  integer $id   文件ID
     * @param  string   $args     回调函数参数
     * @return boolean       false-下载失败，否则输出下载文件
     */
    public function download($root, $id, $callback = null, $args = null){
        /* 获取下载文件信息 */
        $file = $this->find($id);
        if(!$file){
            $this->error = '不存在该文件！';
            return false;
        }

        /* 下载文件 */
        switch ($file['location']) {
            case 0: //下载本地文件
                $file['rootpath'] = $root;
                return $this->downLocalFile($file, $callback, $args);
			case 1: //下载FTP文件
				$file['rootpath'] = $root;
				return $this->downFtpFile($file, $callback, $args);
                break;
            default:
                $this->error = '不支持的文件存储类型！';
                return false;

        }

    }

    /**
     * 检测当前上传的文件是否已经存在
     * @param  array   $file 文件上传数组
     * @return boolean       文件信息， false - 不存在该文件
     */
    public function isFile($file){
        if(empty($file['md5'])){
            throw new \Exception('缺少参数:md5');
        }
        /* 查找文件 */
        $map = array('md5' => $file['md5'],'sha1'=>$file['sha1'],);
        die(json_encode($this->field(true)->where($map)->find()));
        return $this->field(true)->where($map)->find();
    }

    /**
     * 下载本地文件
     * @param  array    $file     文件信息数组
     * @param  callable $callback 下载回调函数，一般用于增加下载次数
     * @param  string   $args     回调函数参数
     * @return boolean            下载失败返回false
     */
    private function downLocalFile($file, $callback = null, $args = null){
        if(is_file($file['rootpath'].$file['savepath'].$file['savename'])){
            /* 调用回调函数新增下载数 */
            is_callable($callback) && call_user_func($callback, $args);

            /* 执行下载 */ //TODO: 大文件断点续传
            header("Content-Description: File Transfer");
            header('Content-type: ' . $file['type']);
            header('Content-Length:' . $file['size']);
            if (preg_match('/MSIE/', $_SERVER['HTTP_USER_AGENT'])) { //for IE
                header('Content-Disposition: attachment; filename="' . rawurlencode($file['name']) . '"');
            } else {
                header('Content-Disposition: attachment; filename="' . $file['name'] . '"');
            }
            readfile($file['rootpath'].$file['savepath'].$file['savename']);
            exit;
        } else {
            $this->error = '文件已被删除！';
            return false;
        }
    }

	/**
	 * 下载ftp文件
	 * @param  array    $file     文件信息数组
	 * @param  callable $callback 下载回调函数，一般用于增加下载次数
	 * @param  string   $args     回调函数参数
	 * @return boolean            下载失败返回false
	 */
	private function downFtpFile($file, $callback = null, $args = null){
		/* 调用回调函数新增下载数 */
		is_callable($callback) && call_user_func($callback, $args);

		$host = C('DOWNLOAD_HOST.host');
		$root = explode('/', $file['rootpath']);
		$file['savepath'] = $root[3].'/'.$file['savepath'];

		$data = array($file['savepath'], $file['savename'], $file['name'], $file['mime']);
		$data = json_encode($data);
		$key = think_encrypt($data, C('DATA_AUTH_KEY'), 600);

		header("Location:http://{$host}/onethink.php?key={$key}");
	}

	/**
	 * 清除数据库存在但本地不存在的数据
	 * @param $data
	 */
	public function removeTrash($data){
		$this->where(array('id'=>$data['id'],))->delete();
	}

}
